package comunicaciones;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Iterator;
import excepciones.JuegoException;
import excepciones.WebServiceException;
import logica.Bando;
import logica.Juego;
import logica.Jugador;
import value_objects.VoCargarPartida;
import value_objects.VoDatosPreinicio;
import value_objects.VoDatosPreinicioCarga;
import value_objects.VoIdioma;
import value_objects.VoInfoJuego;
import value_objects.VoInicioJuego;
import value_objects.VoPartidaListado;
import value_objects.VoTextoIdioma;
import auxiliares.Configuracion;
import auxiliares.Coordenada;
import auxiliares.Global;
import auxiliares.Monitor;

public class WebService {
	private Monitor monitorCrearPartida = new Monitor();
	private Monitor monitorPuerto = new Monitor();
	private Monitor monitorEnviarInfo = new Monitor();
	
	public WebService() { }

	/**
	 * Lista las partidas abiertas, iniciadas o no. Se usa en la interfaz web.
	 * @return VOListaPartidas
	 */
	public VoPartidaListado[] listarPartidas() {
		try {
			monitorEnviarInfo.comienzoLectura();
			VoPartidaListado[] vo = Juego.listarPartidas();
			monitorCrearPartida.terminoLectura();
			return vo;
		} catch (Exception e) {
			e.printStackTrace();
			monitorCrearPartida.terminoLectura();
			return null;
		}
	}

	/**
	 * Crea una nueva partida o juego con un bando
	 * @param _nombre
	 * @param _dificultad
	 * @param _nombreJugador
	 * @param _idioma
	 * @param _nombreBando
	 * @return
	 * @author Maximiliano Anastasiadis
	 */
	public String crearPartida(String _nombrePartida, int _dificultad, String _nombreJugador, 
							   String _idioma, String _nombreBando) {
		String respuesta;
		try {
			monitorCrearPartida.comienzoEscritura();
			if (Juego.existeNombrePartida(_nombrePartida)) {
				respuesta = "ERROR::" + JuegoException.ERROR_PARTIDA_EXISTE;
				monitorCrearPartida.terminoEscritura();
			} else {
				String codigo = Juego.generarCodigoJuego(_nombrePartida);
				
					Juego.getInstance(codigo, _nombrePartida, _dificultad)
						 .agregarJugador(_nombreJugador, _nombreBando, _idioma);
					System.out.println(codigo);
					respuesta = codigo;
					monitorCrearPartida.terminoEscritura();
				
			}
		} catch (JuegoException e) {
			e.printStackTrace();
			respuesta = "ERROR::" + e.getMessage();
			monitorCrearPartida.terminoEscritura();
		} catch (Exception e) {
			e.printStackTrace();
			respuesta = "ERROR::" + JuegoException.ERROR_INESPERADO;
			monitorCrearPartida.terminoEscritura();
		}
		return respuesta;
	}
	
	/**
	 * Elimina una partida y deja libre un slot
	 * @param _codigoPartida
	 * @return String Indicador de suceso o error
	 */
	public String eliminarPartida(String _codigoPartida) {
		String retorno = null;
		try {
			Juego.eliminarPartida(_codigoPartida);
			retorno = "OK";
		} catch (JuegoException e) {
			e.printStackTrace();
			retorno = "ERROR::" + e.getMessage();
		}
		return retorno;
	}
	
	
	/***
	 * agrega un nuevo bando a la partida
	 * @param _nombreJugador
	 * @param _idioma
	 * @param _nombreBando
	 * @param _codigoPartida
	 * @uthor Maximliano Anastasiadis
	 */
	public String agregarJugador(String _nombreJugador, String _idioma, String _nombreBando, 
								 String _codigoPartida) {
		String respuesta = "";
		try {
			monitorCrearPartida.comienzoEscritura();
			Juego.getInstance(_codigoPartida).agregarJugador( _nombreJugador,_nombreBando, _idioma);
			respuesta = "OK";
			monitorCrearPartida.terminoEscritura();
		} catch (JuegoException e) {
			e.printStackTrace();
			respuesta = "ERROR::" + e.getMessage();
			monitorCrearPartida.terminoEscritura();
		} catch (InterruptedException e) {
			e.printStackTrace();
			respuesta = "ERROR::" + e.getMessage();
			monitorCrearPartida.terminoEscritura();
		}
		return respuesta;
	}

	/***
	 * agrega un nuevo bando a la partida cargada
	 * @param _nombreJugador
	 * @param _idioma
	 * @param _nombreBando
	 * @param _codigoPartida
	 * @uthor Maximliano Anastasiadis
	 */
	public VoCargarPartida agregarJugadorPartidaCargada(String _codigoPartidaBando) {
		String codigoPartida = "";
		String nombreBando = "";
		VoCargarPartida respuesta = null;
		try {
			monitorCrearPartida.comienzoEscritura();
			if (_codigoPartidaBando.contains(Bando.BANDO_ROJO)) {
				codigoPartida = _codigoPartidaBando.substring(0, _codigoPartidaBando.length() - 4);
				nombreBando = _codigoPartidaBando.substring(codigoPartida.length(), _codigoPartidaBando.length());
			} else if(_codigoPartidaBando.contains(Bando.BANDO_AZUL)) {
				codigoPartida = _codigoPartidaBando.substring(0, _codigoPartidaBando.length() - 4);
				nombreBando = _codigoPartidaBando.substring(codigoPartida.length(), _codigoPartidaBando.length());
			} else if (_codigoPartidaBando.contains(Bando.BANDO_VERDE)) {
				codigoPartida = _codigoPartidaBando.substring(0, _codigoPartidaBando.length() - 5);
				nombreBando = _codigoPartidaBando.substring(codigoPartida.length(), _codigoPartidaBando.length());
			} else {
				monitorCrearPartida.terminoEscritura();
				throw new WebServiceException(WebServiceException.ERROR_DATOS_CARGA);
			}
			Juego juego = Juego.getInstance(codigoPartida);
			juego.agregarJugadorPartidaCargada(nombreBando);
			String jugador = "";
			Iterator<Jugador> jugadores = juego.getJugadores();
			while (jugadores.hasNext() && jugador == "") {
				Jugador jug = jugadores.next();
				if (jug.getBando().getNombreBando() == nombreBando) {
					jugador = jug.getNombre();
				}
			}
			respuesta = new VoCargarPartida(codigoPartida, jugador, nombreBando);
			monitorCrearPartida.terminoEscritura();
		} catch (Exception e) {
			e.printStackTrace();
			respuesta = null;
			monitorCrearPartida.terminoEscritura();
		}
		return respuesta;
	}

	
	/**
	 * Inicia la partida, posiciona los barcos, comienza el turno
	 * @author Maximiliano Anastasiadis
	 * @param _codigoPartida
	 */
	public String iniciarPartida(String _codigoPartida) {
		String respuesta = "";
		try {
			Juego.getInstance(_codigoPartida).iniciarPartida();
			respuesta = "OK";
		} catch (JuegoException e) {
			e.printStackTrace();
			respuesta = "ERROR::" + e.getMessage();
		}

		return respuesta;
	}
    
	/***
	 * Guarda la partida y genera un xml con la info que se descarga 
	 * el creador de la partida
	 * @param _codigoPartida El código de la partida a guardar
	 * @return Respuesta del método al cliente
	 * @author Maximliano Anastasiadis
	 */
	public String guardarPartida(String _codigoPartida) {
		String respuesta = "";
		try {
			Juego.getInstance(_codigoPartida).guardarPartida();
			respuesta = "OK";
		} catch (JuegoException e) {
			e.printStackTrace();
			respuesta = "ERROR::" + e.getMessage();
		}
		return respuesta;
	}
	
	/***
	 * carga la partida desde un xml, reestableciendo todos los datos de la misma
	 * @author Maximiliano Anastasiadis
	 * @param _codigoPartida
	 * @return
	 */ 
	public VoCargarPartida cargarPartida(String _codigoPartida) {
		try {
			monitorCrearPartida.comienzoEscritura();
			Juego juego = Juego.getInstanceCargarPartida(_codigoPartida);
			VoCargarPartida vo = new VoCargarPartida(juego.getCodigoJuego(), juego.getCreadorPartida(), juego.getJugador(juego.getCreadorPartida()).getBando().getNombreBando());
			monitorCrearPartida.terminoEscritura();
			return vo;
		} catch (JuegoException e) {
			e.printStackTrace();
			monitorCrearPartida.terminoEscritura();
			return null;
		} catch (InterruptedException e) {
			e.printStackTrace();
			monitorCrearPartida.terminoEscritura();
			return null;
		}
	}
	
	/***
	 * Permite a un jugador abandonar la partida. 
	 * @param _codigoPartida
	 * @param nombreBando
	 */ 
	public String abandonarPartida(String _codigoPartida, String nombreJugador) {
		try {
			monitorEnviarInfo.comienzoEscritura();
			Juego.getInstance(_codigoPartida).abandonarPartida(nombreJugador);
			monitorEnviarInfo.terminoEscritura();
			return "OK";
		} catch (Exception e) {
			e.printStackTrace();
			monitorEnviarInfo.terminoEscritura();
			return "ERROR::" + e.getMessage();
		}
	}

	/***
	 * Devuelve una instantánea del estado del juego al cliente.
	 * @param _codigoPartida
	 * @return VoInfoJuego Instánea del juego
	 */
	public VoInfoJuego obtenerInfoJuego(String _codigoPartida, String _bando) {
		try {
			monitorEnviarInfo.comienzoLectura();
			VoInfoJuego vo = Juego.getInstance(_codigoPartida).obtenerInfoJuego(_bando); 
			monitorEnviarInfo.terminoLectura();
			return vo;
		} catch (Exception e) {
			e.printStackTrace();
			monitorEnviarInfo.terminoLectura();
			return null;
		}
	}
	
	/**
	 * Devuelve los datos necesarios para que un bando configure su escenario en el cliente
	 * @param _codigoJuego
	 * @param _bando
	 * @return Instantánea de inicio de juego
	 */
	public VoInicioJuego datosInicio(String _codigoJuego, String _bando) {
		try {
			monitorEnviarInfo.comienzoLectura();
			VoInicioJuego vo = Juego.getInstance(_codigoJuego).datosInicio(_bando);
			monitorEnviarInfo.terminoLectura();
			return vo;
		} catch (Exception e) {
			e.printStackTrace();
			monitorEnviarInfo.terminoLectura();
			return null;
		}
	}
	
	/***
	 * Llamada de disparo de un barco
	 * @param _accion
	 * @param _nombreBando
	 * @param _idTipoBarco
	 * @param _xy
	 * @param _codigoPartida
	 * @param _nombreBarco
	 * @return String OK en caso de éxito, Error::descripción en caso de error
	 */
	public String ataqueBarco(String _codigoPartida, String _nombreJugador,
							  String _nombreBarcoAtaca, int _idArma, int _xO, int _yO, int _xD, int _yD) {
		String respuesta;
		try {
			monitorEnviarInfo.comienzoEscritura();
			Juego.getInstance(_codigoPartida).ataqueBarco(_nombreJugador, _nombreBarcoAtaca, _idArma, _xO, _yO, _xD, _yD);
			respuesta = "OK";
			monitorEnviarInfo.terminoEscritura();
		} catch (JuegoException e) {
			e.printStackTrace();
			respuesta = "ERROR::" + e.getMessage();
			monitorEnviarInfo.terminoEscritura();
		} catch (InterruptedException e) {
			respuesta = "ERROR::" + e.getMessage();
			monitorEnviarInfo.terminoEscritura();
		}
		return respuesta;
	}
	
	/**
	 * Llamada de colisión de arma contra un barco
	 * @param _codigoPartida
	 * @param _nombreBarcoRecibe
	 * @param _nombreBarcoAtaca
	 * @param _idArma
	 * @return String
	 */
	public String colisionArmaBarco(String _codigoPartida, String _nombreBarcoAtaca,
									int _idArma, String _nombreBarcoRecibe) {
		String respuesta;
		try {
			monitorEnviarInfo.comienzoEscritura();
			Juego.getInstance(_codigoPartida).colisionArmaBarco(_nombreBarcoRecibe, _nombreBarcoAtaca, _idArma);
			respuesta = "OK";
			monitorEnviarInfo.terminoEscritura();
		} catch (JuegoException e) {
			e.printStackTrace();
			respuesta = "ERROR"; 
			monitorEnviarInfo.terminoEscritura();
		} catch (InterruptedException e) {
			e.printStackTrace();
			respuesta = "ERROR";
			monitorEnviarInfo.terminoEscritura();
		}
		return respuesta;
	}
	
	/**
	 * Recibe las nuevas posiciones de un barco en el cliente y las asigna a su representación en el servidor.
	 * @param _nombreJugador
	 * @param _xy
	 * @param _codigoPartida
	 * @param _nombreBarco
	 */
	public void movimientoBarco(String _codigoPartida, String _nombreJugador,
								String _nombreBarco, int _x, int _y, int _angulo) {
		try {
			monitorEnviarInfo.comienzoEscritura();
			Juego.getInstance(_codigoPartida).movimientoBarco(_nombreJugador, _nombreBarco, new Coordenada(_x, _y), _angulo);
			monitorEnviarInfo.terminoEscritura();
		} catch (JuegoException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
			monitorEnviarInfo.terminoEscritura();
		}
	}
	
	/**
	 * Recibe el nuevo ángulo de un barco en el cliente y las asigna su representación en el servidor  
	 * @param _nombreJugador
	 * @param _codigoPartida
	 * @param _xy
	 * @param _nombreBarco
	 */
	public void virarBarco(String _codigoPartida, String _nombreJugador, String _nombreBarco, int _angulo) {
		try {
			monitorEnviarInfo.comienzoEscritura();
			Juego.getInstance(_codigoPartida).virarBarco(_nombreJugador, _nombreBarco, _angulo);
			monitorEnviarInfo.terminoEscritura();
		} catch (JuegoException e) {
			e.printStackTrace();
			monitorEnviarInfo.terminoEscritura();
		} catch (InterruptedException e) {
			e.printStackTrace();
			monitorEnviarInfo.terminoEscritura();
		}
	}
	
	/**
	 * Devuelve los datos de espera de inicio de una partida
	 * @param _codigoPartida
	 * @return
	 */
	public VoDatosPreinicio datosPreinicio(String _codigoPartida) {
		try {
			monitorCrearPartida.comienzoEscritura();
			VoDatosPreinicio vo = Juego.getInstance(_codigoPartida).datosPreinicio();
			monitorCrearPartida.terminoEscritura();
			return vo;
		} catch (JuegoException e) {
			e.printStackTrace();
			monitorCrearPartida.terminoEscritura();
			return null;
		} catch (InterruptedException e) {
			e.printStackTrace();
			monitorCrearPartida.terminoEscritura();
			return null;
		}
	}

	/**
	 * 
	 * @param _codigoPartida
	 * @return
	 */
	public VoDatosPreinicioCarga datosPreinicioCarga(String _codigoPartida) {
		try {
			monitorCrearPartida.comienzoEscritura();
			VoDatosPreinicioCarga vo = Juego.getInstance(_codigoPartida).datosPreinicioCarga();
			monitorCrearPartida.terminoEscritura();
			return vo;
		} catch (JuegoException e) {
			e.printStackTrace();
			monitorCrearPartida.terminoEscritura();
			return null;
		} catch (InterruptedException e) {
			e.printStackTrace();
			monitorCrearPartida.terminoEscritura();
			return null;		}
	}

	/**
	 * 
	 * @return 
	 */
	public void anularDisparo(String _codigoPartida, String nombreBarco, Integer idArma) {
		try {
			monitorEnviarInfo.comienzoEscritura();
			Juego.getInstance(_codigoPartida).eliminarDisparoActivo(nombreBarco, idArma);
			monitorEnviarInfo.terminoEscritura();
		} catch (JuegoException e) {
			e.printStackTrace();
			monitorEnviarInfo.terminoEscritura();
		} catch (InterruptedException e) {
			e.printStackTrace();
			monitorEnviarInfo.terminoEscritura();
		}
	}

	/**
	 * Devuelve toda la informacion de idiomas
	 * @return VoIdioma[] Arreglo con todos los idiomas: su codigo, nombre y diccionario, null en caso de error
	 */
	public synchronized VoIdioma[] textosIdiomas() {
		VoIdioma[] retorno = null;
		try {
			Global.getInstance().setProperty(Global.SILVER_RIVER_PATH, System.getProperty("user.home") + Juego.RUTA_JUEGO);
			
			Configuracion idiomas = Configuracion.getInstancia(Configuracion.CONFIGURACION_IDIOMAS);
			Enumeration<Object> clavesIdiomas = idiomas.getKeys();
			retorno = new VoIdioma[idiomas.getCount()];
			Integer i = 0;

			while (clavesIdiomas.hasMoreElements()) {
				String nombreIdioma = (String) clavesIdiomas.nextElement();
				Configuracion idioma;
				
				idioma = Configuracion.getInstancia(Configuracion.rutaArchivoIdiomas(nombreIdioma));
				Enumeration<Object> claves = idioma.getKeys();
				VoTextoIdioma[] textos = new VoTextoIdioma[idioma.getCount()];
				Integer j = 0;
				while (claves.hasMoreElements()) {
					String clave = (String) claves.nextElement();
					textos[j++] = new VoTextoIdioma(clave, idioma.getProperty(clave).trim());
				}
				retorno[i++] = new VoIdioma(nombreIdioma, idiomas.getProperty(nombreIdioma), textos);
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			retorno = null;
		} catch (IOException e) {
			retorno = null;
		}
		return retorno;
	}
	
	/**
	 * Repara un barco cuando entra en un puerto.
	 * @param _codigoPartida
	 * @param _idPuerto
	 * @param _idBarco
	 * @param _reparacion
	 * @return
	 */
	public String repararEnPuerto(String _codigoPartida, Integer _idPuerto, String _idBarco, String _reparacion) {
		String respuesta;
		try {
			monitorPuerto.comienzoEscritura();
			Juego.getInstance(_codigoPartida).repararEnPuerto(_idPuerto, _idBarco, _reparacion);
			respuesta = "OK";
			monitorPuerto.terminoEscritura();
		} catch (Exception e) {
			e.printStackTrace();
			respuesta = "ERROR::" + e.getMessage();
			monitorPuerto.terminoEscritura();
		}
		return respuesta;
	}
}
